package com.lin.filegraph.utils.compdg;

/**
 * @description:
 * @author: linhuaixu
 * @time: 2023/5/18 15:38
 */
import com.lin.filegraph.utils.cluster.Cluster;
import com.lin.filegraph.utils.comcheck.ArcOptimize;
import com.lin.filegraph.utils.componentRule.ComponentDepend;
import com.lin.filegraph.utils.filedg.CouplingType;
import com.lin.filegraph.utils.model.ComponentModule;
import com.lin.filegraph.utils.model.Module;
import com.lin.filegraph.utils.storage.ModuleStorage;

import java.util.*;
import java.util.List;

public class ComponentGraph {
    private String projectPath;
    List<ComponentNode> listOfComps;
    List<ComponentEdge> listOfCompEdges;
    int lastNum =0;//上个项目最大的编号
    int typeNum = CouplingType.values().length;//依赖类型总数
    double sumDependStrength;//总的依赖强度
    int totalSize;//文件总数量
    int modelSize;

    List<Module> comList = new LinkedList<>();//记录模块从属关系
    List<ComponentModule> fileList = new ArrayList<>();//模块文件从属图
    List<ComponentDepend> comdList = new LinkedList<>();//模块间依赖图

    public void cacheModuleGraph(ComponentGraph graph) {
        ModuleStorage.cacheModuleGraph(graph, graph.comList, graph.fileList, graph.comdList, 0);
    }

    public void executeCluter(ComponentGraph graph) {
        Cluster.executeCluter(graph, graph.comList, graph.fileList, graph.comdList);
    }

    public void executeOptimize(String excelPath, String projectPath, ComponentGraph graph) {
        ArcOptimize.executeOptimize(excelPath, projectPath, graph,graph.comList,graph.fileList);
    }

    public List<Module> getComList() {
        return comList;
    }

    public void setComList(List<Module> comList) {
        this.comList = comList;
    }

    public void setFileList(List<ComponentModule> fileList) {
        this.fileList = fileList;
    }

    public void setComdList(List<ComponentDepend> comdList) {
        this.comdList = comdList;
    }

    //	List<ComponentEdge> listEdgesFromDir;
/*	int level=0;
	public void setLevel(int level){
		this.level=level;
	}
	public int getLevel(){
		return level;
	}*/
    public ComponentNode findNodeById(int id){
        for(ComponentNode cnode:listOfComps){
            if(id==cnode.getID())
                return cnode;
        }
        return null;
    }
    public ComponentGraph() {
        listOfCompEdges = new ArrayList<>();
        listOfComps = new ArrayList<>();
    }

    public ComponentGraph(String projectPath) {
        this();
        this.projectPath = projectPath;
    }

    public ComponentGraph(ComponentGraph mGraph) {
        this.projectPath = mGraph.projectPath;
        this.lastNum = mGraph.lastNum;
        this.typeNum = mGraph.typeNum;
        this.sumDependStrength = mGraph.sumDependStrength;
        this.totalSize = mGraph.totalSize;
        this.modelSize = mGraph.modelSize;
        this.listOfComps = new ArrayList<>();
        for (ComponentNode node: mGraph.getAllComponents()) {
            listOfComps.add(node);
        }
        this.listOfCompEdges = new ArrayList<>();
        for (ComponentEdge edge: mGraph.getAllComponentEdges()) {
            listOfCompEdges.add(edge);
        }
    }
/*	private ComponentGraph(String projectPath, Connection conn, long projID, long verID) {
		this();
		this.projectPath = projectPath;
		getCompsFromDB(conn, projID, verID);
		getCompEdgesFromDB(conn, projID, verID);
	}*/

    /**
     * 创建工程路径为projectPath的组件图
     *
     * @paramroot
     * @return
     */
/*	public static ComponentGraph createCompDGByDB(String projectPath, Connection conn, long projID, long verID) {
		// TODO Auto-generated component stub
		ComponentGraph compdg = new ComponentGraph(projectPath, conn, projID, verID);
		return compdg;
	}*/

    public static ComponentGraph createCompDGByDB(String projectPath) {
        // TODO Auto-generated component stub
        ComponentGraph compdg = new ComponentGraph(projectPath);
        return compdg;
    }

/*	public void getCompsFromDB(Connection conn, long projID, long verID) {
		List<String> compGroupNames = ComponentModuleDao.queryAllComp(conn, projID, verID);
		for(String compGroupName : compGroupNames) {
			ComponentNode component = new ComponentNode(compGroupName, projectPath);
			listOfComps.add(component);

			List<String> listOfFiles = ComponentModuleDao.queryAllFilesOfComp(conn, compGroupName, projID, verID);
			for (String strFilePath : listOfFiles) {
				component.addFile(strFilePath);
			}
		}
	}

	public void getCompEdgesFromDB(Connection conn, long projID, long verID) {
		List<ComponentDepDBModel> componentDepDBModels = null;
		try {
			componentDepDBModels = ComponentDepDao.queryAllComponentDeps(conn, projID, verID);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		if (componentDepDBModels != null) {
			for (ComponentDepDBModel componentDepDBModel : componentDepDBModels) {
				ComponentNode preComponent = getComponentByName(componentDepDBModel.getPreComponent());
				ComponentNode postComponent = getComponentByName(componentDepDBModel.getPostComponent());
				if(preComponent != null && postComponent != null) {
					ComponentEdge componentEdge = new ComponentEdge(preComponent, postComponent);
					listOfCompEdges.add(componentEdge);
				}
			}
		}
	}*/

    public void addCompNode(ComponentNode compNode) {
        listOfComps.add(compNode);
    }

    public void addCompEdge(ComponentEdge compEdge) {
        listOfCompEdges.add(compEdge);
    }

    public List<ComponentNode> getAllComponents() {
        return listOfComps;
    }

    public List<ComponentEdge> getAllComponentEdges() {
        return listOfCompEdges;
    }


    public ComponentNode getComponentByName(String strComponentName) {
        for (ComponentNode component : listOfComps) {
            if (component.getStrComponentName().equals(strComponentName)) {
                return component;
            }
        }
        return null;
    }

    public ComponentNode getComponentByID(int id) {
        for (ComponentNode component : getAllComponents()) {
            if (component.getID()==id) {
                return component;
            }
        }
        return null;
    }


    public ComponentNode getComponentByFileRelPath(String fileRelPath) {
        for (ComponentNode component : listOfComps) {
            if (component.getAllFiles().contains(fileRelPath)) {
                return component;
            }
        }
        return null;
    }


    public ComponentNode getComponentByDirName(String strDirName) {
        for (ComponentNode component : listOfComps) {
            if (component.getAllDirs().contains(strDirName) || (strDirName.equals("") && component.getAllDirs().contains("-1"))) {
                return component;
            }
        }
        return null;
    }

    public List<ComponentEdge> getOutEdgesOfComp(ComponentNode currentComponent) {
        List<ComponentEdge> outEdges = new ArrayList<>();
        for (ComponentEdge edge : getAllComponentEdges()) {
            if (edge.getPreComponent().equals(currentComponent)) {
                outEdges.add(edge);
            }
        }
        return outEdges;
    }

    public List<ComponentEdge> getInEdgesOfComp(ComponentNode currentComponent) {
        List<ComponentEdge> inEdges = new ArrayList<>();
        for (ComponentEdge edge : getAllComponentEdges()) {
            if (edge.getPostComponent().equals(currentComponent)) {
                inEdges.add(edge);
            }
        }
        return inEdges;
    }

    public ComponentEdge getCompEdge(ComponentNode preComponent, ComponentNode postComponent) {
        for (ComponentEdge edge : listOfCompEdges) {
            if (edge.getPreComponent().equals(preComponent) && edge.getPostComponent().equals(postComponent)) {
                return edge;
            }
        }
        return null;
    }
    public boolean getCompEdge(String preComponent, String postComponent) {
        for (ComponentEdge edge : listOfCompEdges) {
            if (edge.getPreComponent().getStrComponentName().equals(preComponent) && edge.getPostComponent().getStrComponentName().equals(postComponent)) {
                return true;
            }
        }
        return false;
    }
    public ComponentEdge getCompEdgeReturnEdge(String preComponent, String postComponent) {
        for (ComponentEdge edge : listOfCompEdges) {
            if (edge.getPreComponent().getStrComponentName().equals(preComponent) && edge.getPostComponent().getStrComponentName().equals(postComponent)) {
                return edge;
            }
        }
        return null;
    }
    public double getCompEdgeDependence(ComponentNode cn1, ComponentNode cn2) {
        double depend=0;
        for (ComponentEdge edge : listOfCompEdges) {
            if (edge.getPreComponent().equals(cn1) && edge.getPostComponent().equals(cn2)) {
                depend+=edge.getDependence();
            }
            if (edge.getPreComponent().equals(cn2) && edge.getPostComponent().equals(cn1)) {
                depend+=edge.getDependence();
            }
        }
        return depend;
    }

    public List<ComponentNode> getPreComponents(ComponentNode currentComponent) {
        List<ComponentNode> preComps = new ArrayList<>();
        List<ComponentEdge> inEdges = getInEdgesOfComp(currentComponent);
        for (ComponentEdge inEdge : inEdges) {
            preComps.add(inEdge.getPreComponent());
        }
        return preComps;
    }

    public List<ComponentNode> getPostComponents(ComponentNode currentComponent) {
        List<ComponentNode> postComps = new ArrayList<>();
        List<ComponentEdge> outEdges = getOutEdgesOfComp(currentComponent);
        for (ComponentEdge outEdge : outEdges) {
            postComps.add(outEdge.getPostComponent());
        }
        return postComps;
    }

    /**
     * 获取邻居节点，即出入
     */
    public Set<ComponentNode> getNeighbours(ComponentNode currentComponent) {
        Set<ComponentNode> Comps = new TreeSet<>();
        List<ComponentEdge> outEdges = getOutEdgesOfComp(currentComponent);
        for (ComponentEdge outEdge : outEdges) {
            Comps.add(outEdge.getPostComponent());
        }
        List<ComponentEdge> inEdges = getInEdgesOfComp(currentComponent);
        for (ComponentEdge inEdge : inEdges) {
            Comps.add(inEdge.getPreComponent());
        }
        return Comps;
    }

    /**
     * 获取邻居节点，即出入边
     */
    public List<ComponentEdge> getNeighboursEdge(ComponentNode currentComponent) {
        List<ComponentEdge> outEdges = getOutEdgesOfComp(currentComponent);
        List<ComponentEdge> inEdges = getInEdgesOfComp(currentComponent);
        outEdges.addAll(inEdges);
        return outEdges;
    }

    public List<ComponentNode> getDepthPostComponents(ComponentNode currentComponent) {
        List<ComponentNode> postComps = new ArrayList<>();
        List<ComponentEdge> outEdges = getOutEdgesOfComp(currentComponent);
        for (ComponentEdge outEdge : outEdges) {
            if(!outEdge.getPostComponent().isIscheck())
                postComps.add(outEdge.getPostComponent());
        }
        return postComps;
    }
    public List<ComponentNode> getDepthPreComponents(ComponentNode currentComponent) {
        List<ComponentNode> preComps = new ArrayList<>();
        List<ComponentEdge> inEdges = getInEdgesOfComp(currentComponent);
        for (ComponentEdge inEdge : inEdges) {
            if(!inEdge.getPostComponent().isIscheck())
                preComps.add(inEdge.getPreComponent());
        }
        return preComps;
    }

    public void mergeNodes(ComponentNode node1, ComponentNode node2) {
//		System.out.println("merge..."+node1.getStrComponentName()+"-"+node2.getStrComponentName());
        if (!(listOfComps.contains(node1) && listOfComps.contains(node2)) || node1.equals(node2)) {
            System.out.println(listOfComps.contains(node1));
            System.out.println(listOfComps.contains(node2));
            ComponentEdge componentEdge =getCompEdge(node1, node2);
            if(componentEdge==null)
                System.out.println("null");

            return;
        }
        node1.getLay_components().add(node2.getStrComponentName());
        node1.getLay_components().addAll(node2.getLay_components());
        System.out.println("mergeSize..."+node1.getAllFiles().size()+"-"+node2.getAllFiles().size());
        updateNodeInfo(node1, node2);

        mergeRelatedEdge(node1, node2,listOfCompEdges);

        Set<ComponentEdge> setOfCompEdges = new LinkedHashSet<>(listOfCompEdges);
        listOfCompEdges = new ArrayList<>(setOfCompEdges);
        Collections.sort(listOfCompEdges);

//		if (node2.getAllFiles().size() / node1.getAllFiles().size() > Threshold.preReName) {
//			if (node2.isFromPre()) {
//				node1.setFromPre(true);
//				node1.setStrComponentName(node2.getStrComponentName());
//				node1.setNum(node2.getNum());
//			} else {
//				node1.setFromPre(false);
//
//			}
//		}
        listOfComps.remove(node2);
//		node1.reComName(ComRename.getComNameByDir(node1));
        node2=null;

    }

    private void updateNodeInfo(ComponentNode node1, ComponentNode node2) {
        node1.getLay_components().addAll(node2.getLay_components());
        node1.getAllFiles().addAll(node2.getAllFiles());
        node1.getAllDirs().addAll(node2.getAllDirs());
        node1.addLocOfCom(node2.getLocOfCom());
        if(node2.getPreNum()>node1.getPreNum()){
            node1.setCenternode(node2.getCenternode());
            node1.setPreNum(node2.getPreNum());
        }

        node1.setRegion(node1.isRegion() ||node2.isRegion());
        node1.setFeature(node1.isFeature()||node2.isFeature());
        if(node2.getCenternode()!=null&&node2.getCenternode().equals("")){
            node1.setCenternode(node2.getCenternode());
        }
    }

    private void mergeRelatedEdge(ComponentNode node1, ComponentNode node2,List<ComponentEdge> listOfEdges) {
        for (int i = listOfEdges.size() - 1; i >= 0; i--) {
            ComponentEdge edge = listOfEdges.get(i);
            if (edge.getPreComponent().equals(node2)) {
                if (edge.getPostComponent().equals(node1)) {
                    listOfEdges.remove(edge);
                } else {
                    ComponentNode postComp = edge.getPostComponent();
                    ComponentEdge node1Edge = getCompEdge(node1, postComp);
                    if (node1Edge == null) {
                        edge.setPreComponent(node1);
                    } else {
                        updateEdgeInfo(edge, node1Edge);
                        listOfEdges.remove(edge);
                    }
                }
            }
            if (edge.getPostComponent().equals(node2)) {
                if (edge.getPreComponent().equals(node1)) {
                    listOfEdges.remove(edge);
                } else {
                    ComponentNode preComp = edge.getPreComponent();
                    ComponentEdge node1Edge = getCompEdge(preComp, node1);
                    if (node1Edge == null) {
                        edge.setPostComponent(node1);
                    } else {
                        updateEdgeInfo(edge, node1Edge);
                        listOfEdges.remove(edge);
                    }
                }
            }
        }
    }

    public void updateEdgeInfo(ComponentEdge edge, ComponentEdge node1Edge) {
        // 合并依赖强度
        node1Edge.setDependence(node1Edge.getDependence() + edge.getDependence());
        node1Edge.setDependNum(edge.getDependNum()+node1Edge.getDependNum());
        if (edge.isHasInherOrCombin())
            node1Edge.setHasInherOrCombin(true);
        if (edge.getCouplingTypes() != null) {
            if(node1Edge.getCouplingTypes()!=null){
                String str =addType(edge.getCouplingTypes(), node1Edge.getCouplingTypes());
                node1Edge.setCouplingTypes(str);
            }else{
                node1Edge.setCouplingTypes(edge.getCouplingTypes());
            }
        }
    }

    private String addType(String type1,String type2){
//		System.out.println(type1);
        String[] typeS1 =type1.split(",");
        String[] typeS2 =type2.split(",");
        StringBuffer sBuffer = new StringBuffer();
        for(int i=0;i<typeNum;i++){
//			System.out.println(typeS1[i]);
            int t= Integer.parseInt(typeS1[i])+Integer.parseInt(typeS2[i]);
            sBuffer.append(t);
            if(i!=typeNum-1)
                sBuffer.append(",");
        }
        return sBuffer.toString();
    }

    public int mergeNodes(List<ComponentNode> nodes) {
        int ret = 0;

        if(nodes== null|| nodes.size() <= 1) {
            return 0;
        }

        List<ComponentNode> nodes1 = new ArrayList<>();
        for(ComponentNode node : nodes) {
            if(!nodes1.contains(node)) {
                nodes1.add(node);
            }
        }
        if(nodes1.size() <= 1) {
            return 0;
        }
        ComponentNode node1 = nodes1.get(0);
        for(int i = 1; i < nodes1.size(); i ++) {
            ComponentNode nextNode = nodes1.get(i);
            updateNodeInfo(node1, nextNode);
            listOfComps.remove(nextNode);
//			System.out.println("merge..."+node1.getStrComponentName()+"-"+nextNode.getStrComponentName());
//			System.out.println("mergeSize..."+node1.getAllFiles().size()+"-"+nextNode.getAllFiles().size());
            ret ++;

        }
        mergeRelatedEdge(nodes1);
        Set<ComponentEdge> setOfCompEdges = new LinkedHashSet<>(listOfCompEdges);
        listOfCompEdges = new ArrayList<>(setOfCompEdges);
        Collections.sort(listOfCompEdges);
        return ret;
    }

    private void mergeRelatedEdge(List<ComponentNode> nodes) {
        List<ComponentNode> deletedNodes = nodes.subList(1, nodes.size());
        ComponentNode node1 = nodes.get(0);
        for (int i = listOfCompEdges.size() - 1; i >= 0; i--) {
            ComponentEdge edge = listOfCompEdges.get(i);
            if(deletedNodes.contains(edge.getPreComponent()) && deletedNodes.contains(edge.getPostComponent())) {
                listOfCompEdges.remove(edge);
                continue;
            }
            else if (deletedNodes.contains(edge.getPreComponent())) {
                if (edge.getPostComponent().equals(node1)) {
                    listOfCompEdges.remove(edge);
                } else {
                    ComponentNode postComp = edge.getPostComponent();
                    ComponentEdge node1Edge = getCompEdge(node1, postComp);
                    if (node1Edge == null) {
                        edge.setPreComponent(node1);
                    } else {
                        updateEdgeInfo(edge, node1Edge);
                        listOfCompEdges.remove(i);
                    }
                }
            }
            else if (deletedNodes.contains(edge.getPostComponent())) {
                if (edge.getPreComponent().equals(node1)) {
                    listOfCompEdges.remove(i);
                } else {
                    ComponentNode preComp = edge.getPreComponent();
                    ComponentEdge node1Edge = getCompEdge(preComp, node1);
                    if (node1Edge == null) {
                        edge.setPostComponent(node1);
                    } else {
                        updateEdgeInfo(edge, node1Edge);
                        listOfCompEdges.remove(i);
                    }
                }
            }
        }
        for (int i = listOfCompEdges.size() - 1; i >= 0; i--) {
            if(listOfCompEdges.get(i).getPreComponent().equals(listOfCompEdges.get(i).getPostComponent())) {
                listOfCompEdges.remove(i);
            }
        }
    }

    public boolean isCorrect() {
        for(ComponentEdge edge : listOfCompEdges) {
            if(!(listOfComps.contains(edge.getPreComponent()) && listOfComps.contains(edge.getPostComponent()))) {
                System.out.println("pre:" + edge.getPreComponent() + " "+ listOfComps.contains(edge.getPreComponent()));
                System.out.println("post:" + edge.getPostComponent()+ " "+ listOfComps.contains(edge.getPostComponent()));
                return false;
            }
        }
        return true;
    }

    public void setListOfComps(List<ComponentNode> listOfComps) {
        this.listOfComps = listOfComps;
    }

    public void setListOfCompEdges(List<ComponentEdge> listOfCompEdges) {
        this.listOfCompEdges = listOfCompEdges;
    }


    public double getSumDependStrength() {
        return sumDependStrength;
    }

    public void setSumDependStrength(double dirDependStrength) {
        this.sumDependStrength = dirDependStrength;
    }

    public void setSumDependStrength() {
        double dirDependStrength=0;
        for(ComponentEdge edge:listOfCompEdges)
            dirDependStrength+=edge.getDependence();
        this.sumDependStrength = dirDependStrength;
    }

    public void sort() {
        getAllComponents().sort(new Comparator<ComponentNode>() {
            @Override
            public int compare(ComponentNode o1, ComponentNode o2) {
                // TODO Auto-generated method stub
                return o1.compareTo(o2);
            }
        });
        getAllComponentEdges().sort(new Comparator<ComponentEdge>() {
            @Override
            public int compare(ComponentEdge o1, ComponentEdge o2) {
                // TODO Auto-generated method stub
                return o1.compareTo(o2);
            }
        });
    }

    /**
     * @return the projectPath
     */
    public String getProjectPath() {
        return projectPath;
    }

    /**
     * @param projectPath
     *            the projectPath to set
     */
    public void setProjectPath(String projectPath) {
        this.projectPath = projectPath;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        for (ComponentNode node : this.getAllComponents()) {
            sb.append(node + "\n");
        }
        for (ComponentEdge edge : this.getAllComponentEdges()) {
            sb.append(edge + "\n");
        }
        return sb.toString();
    }

    public void displayallfiles() {
        for (ComponentNode node : listOfComps) {
            for (String str : node.getAllFiles()) {
                System.out.println(str);
            }
        }
    }

    //-------------------------------------//
    public void displayGraph() {
        System.out.println("打印组件图：");
        System.out.println("组件总数：" + listOfComps.size());
        for (ComponentNode Node : listOfComps) {
            System.out.println(Node.getStrComponentName() + "-" + Node.getAllFiles().size() + ":");
            if (Node.isRegion()) {
                System.out.print("~~~~");
            }
            for (String str : Node.getAllFiles()) {
                System.out.println("	" + str);
            }
        }
        System.out.println("组件图边总数：" + listOfCompEdges.size());
    }

    public void setNumBer() {
        for (ComponentNode cn : listOfComps) {
//			if(lastNum==-1){
//				num++;
//				cn.setID(num);
//			}else{
//				// 判断是不是前版本保留
//				if (!cn.isFromPre()) {
//					num++;
//					ComponentNode tNode =getComponentByNum(num);
//					while (tNode != null&&tNode.isFromPre()) {
//						num++;
//						tNode =getComponentByNum(num);
//					}
//					cn.setID(num);
//				}
//			}
            cn.setID(lastNum++);
        }
    }

    public int getLastNum() {
        return lastNum;
    }

    public int addLastNum() {
        return lastNum++;
    }


    public void setAllCheck() {
        for (ComponentNode cn : listOfComps) {
            cn.setIscheck(false);
        }
    }
    public void setAllRegion() {
        for (ComponentNode cn : listOfComps) {
            cn.setRegion(false);
        }
    }


    public List<ComponentEdge> getComEdgesIncludeDir(){
//		List<ComponentEdge> listEdges=new ArrayList<>(listOfCompEdges);
//		listEdges.addAll(listEdgesFromDir);
        return listOfCompEdges;
    }

    public ComponentNode getComponentByNum(int num) {
        for (ComponentNode component : listOfComps) {
            if (component.getID() == num) {
                return component;
            }
        }
        return null;
    }



    /**
     * 节点是否是独立节点
     */
    public boolean isSeprate(ComponentNode cn) {
        if (this.getPostComponents(cn).isEmpty() && this.getPreComponents(cn).isEmpty())
            return true;
        else
            return false;
    }

    /**
     * 计算当前组件图中，非独立组件个数
     */
    public int getNEmptyNum() {
        int sumSeparate = listOfComps.size();
        for (ComponentNode cn : listOfComps) {
            if (isSeprate(cn))
                sumSeparate--;
        }
        return sumSeparate;
    }

    public int getTotalSize() {
        return totalSize;
    }

    public void setTotalSize(int regEdgeNum) {
        this.totalSize = regEdgeNum;
    }

    public int getModelSize() {
        return modelSize;
    }

    public void setModelSize(int modelSize) {
        this.modelSize = modelSize;
    }



}

